package com.shuiyun.jtt.handler;

import cn.hutool.core.util.ObjectUtil;
import com.shuiyun.jtt.anno.Message;
import com.shuiyun.jtt.enums.Constant;
import com.shuiyun.jtt.message.Header;
import com.shuiyun.jtt.message.JTMessage;
import com.shuiyun.jtt.utils.ByteUtil;
import com.shuiyun.jtt.utils.SessionKey;
import com.shuiyun.jtt.utils.codec.CoderDecoderUtil;
import io.netty.buffer.*;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.util.ByteProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * @author shuiyun
 * @description 消息编解码器 <p>使用该消息编解码器的前提必须先解决黏包和半包问题</p>
 * @date 2022-04-18 23:45
 */
@Slf4j
@ChannelHandler.Sharable
@Component
public class MessageCodecHandler extends MessageToMessageCodec<ByteBuf, JTMessage> {

    private ByteBuf sessionBuff = ByteBufAllocator.DEFAULT.buffer();
    private static final ByteProcessor searcher = value -> !(value == 0x7d || value == 0x7e);

    /**
     * 消息编码 将收到的JTMessage 编码成ByteBuf
     *
     * @param ctx 上下文
     * @param msg 交通部消息
     * @param out 输出数据
     * @author shuiyun
     * @date 2022/4/18
     */
    @Override
    public void encode(ChannelHandlerContext ctx, JTMessage msg, List<Object> out) throws Exception {
        Header header = msg.getHeader();
        if (ObjectUtil.isNull(header)) {
            Message annotation = msg.getClass().getAnnotation(Message.class);
            if (ObjectUtil.isNotNull(annotation)) {
                log.error(Arrays.toString(annotation.value()) + "未设置消息头，消息无法发送");
            } else {
                log.error("未设置消息头，消息无法发送");
            }
            return;
        }
        // 根据消息转换为字节码
        ByteBuf bodyBuf = null;
        try {
            bodyBuf = CoderDecoderUtil.obj2ByteBuf(msg);
        } catch (Exception e) {
            log.error("数据编码失败", e);
            throw new RuntimeException("数据编码失败");
        }

        // 如果最大长度 小于1024字节 则直接发送
        if (bodyBuf.readableBytes() < Constant.BODY_MAX_LENGTH) {
            ByteBuf result = getResult(header, bodyBuf);
            out.add(result);
            return;
        }
        // 如果最大长度超过了最大长度1024 则需要进行分包处理
        List<ByteBuf> byteBufList = ByteUtil.slice(bodyBuf, Constant.BODY_MAX_LENGTH);
        header.setSubpackage(true);
        header.setPackageTotal(byteBufList.size());
        for (int i = 0; i < byteBufList.size(); i++) {
            header.setPackageNo(i + 1);
            ByteBuf result = getResult(header, byteBufList.get(i));
            out.add(result);
        }
    }


    /**
     * 消息解码器 将收到的ByteBuf解码成JTMessage消息
     *
     * @param ctx 上下文
     * @param msg 收到的ByteBuf数据
     * @param out 解码好的数据
     * @author shuiyun
     * @date 2022/4/18
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        if (msg.readableBytes() < 1) {
            return;
        }
        // 按照协议进行字符转码
        msg = escape(msg);
        // BCC校验
        boolean verify = ByteUtil.verify(msg);
        if (!verify) {
            log.error("校验码错误:{}", ByteBufUtil.hexDump(msg));
            return;
        }
        log.debug(">>> 接收到消息：{}", ByteBufUtil.hexDump(msg));
        // 去除BCC校验码
        msg = msg.slice(0, msg.readableBytes() - 1);
        Header header = CoderDecoderUtil.byteBuf2Obj(msg, Header.class);
        // 如果需要分包
        if (header.isSubpackage()) {
            sessionBuff.writeBytes(msg);
            // 如果当前包 小于总包数 则进行累加
            if (header.getPackageNo() < header.getPackageTotal()) {
                return;
            }
            msg = sessionBuff;
            sessionBuff = ByteBufAllocator.DEFAULT.buffer();
        }


        Class<?> clazz = SessionKey.instance.getMessageMap().get(header.getMessageId());
        JTMessage message = null;
        if (null == clazz) {
            message = new JTMessage();
        } else {
            message = (JTMessage) CoderDecoderUtil.byteBuf2Obj(msg, clazz);
        }
        message.setHeader(header);
        out.add(message);
    }

    /**
     * 根据头信息和数据体信息生成组装好的数据
     *
     * @param header  消息头
     * @param bodyBuf 消息体
     * @return 拼装好的数据
     * @author shuiyun
     * @date 2022-04-20 17:36
     */
    private ByteBuf getResult(Header header, ByteBuf bodyBuf) {
        ByteBuf result = Unpooled.buffer();
        header.setBodyLength(bodyBuf.readableBytes());
        ByteBuf headerBuf = CoderDecoderUtil.obj2ByteBuf(header);
        // 合并buf
        CompositeByteBuf tempBuff = ByteBufAllocator.DEFAULT.compositeBuffer();
        tempBuff.addComponents(true, headerBuf, bodyBuf);
        // 获取校验码
        byte bcc = ByteUtil.bcc(tempBuff);
        tempBuff.writeByte(bcc);
        // 数据转码
        ByteBuf buffer = escape(tempBuff);
        // 数组组装 0x7e+头+数据体+bcc+0x7e
        result.writeByte(Constant.START_END);
        result.writeBytes(buffer);
        result.writeByte(Constant.START_END);

        log.info("<<< 推送消息：{}", ByteBufUtil.hexDump(result));
        return result;
    }

    /**
     * 字节码转义
     *
     * @param byteBuf 等待转义的数据
     * @return 转义完成的数据
     * @author shuiyun
     * @date 2022/4/23 20:17
     */
    private ByteBuf escape(ByteBuf byteBuf) {
        ByteBuf buffer = ByteUtil.escape(byteBuf, Constant.TAG_ONE, Unpooled.wrappedBuffer(Constant.TAG_TWO));
        return ByteUtil.escape(buffer, Constant.TAG_THREE, Unpooled.wrappedBuffer(Constant.TAG_FOUR));
    }

    /**
     * 字节码反转义
     *
     * @param msg 等待反转义的数据
     * @return 反转义好的数据
     * @author shuiyun
     * @date 2022/4/23 20:18
     */
    private ByteBuf unEscape(ByteBuf msg) {
        msg = ByteUtil.unEscape(msg, Constant.TAG_FOUR, Unpooled.wrappedBuffer(new byte[]{Constant.TAG_THREE}));
        return ByteUtil.unEscape(msg, Constant.TAG_TWO, Unpooled.wrappedBuffer(new byte[]{Constant.TAG_ONE}));
    }


}
